<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace crcl\base\mvc\router;

interface IConcrete
{
  /**
   * Returns controller
   *
   * @return  string
   */
  public function getController();

  /**
   * Returns action
   *
   * @return  string
   */
  public function getAction();

  /**
   * Returns group
   *
   * @return  string
   */
  public function getGroup();

  /**
   * Returns method
   *
   * @return  string
   */
  public function getMethod();

  /**
   * Returns method
   *
   * @return  string
   */
  public function getResource();

  /**
   * Returns view
   *
   * @return string
   */
  public function getView();

  /**
   * Returns TRUE if requester is xhtml capable
   */
  public function hasXhtml();

  /**
   * Identifies controller, action, group, resource, method and view from request
   * order can be overriden in app config
   * [mvc]
   * router[1] = controller
   * router[2] = controller:action
   * router[3] = group:controller:action
   * router[4] = group:controller:action:resource
   *
   * you can override default scheme on special urls
   * router[logs:event] = group:action
   *
   * HTTP:
   * /controller
   * /controller/action
   * /group/controller/action
   * /group/controller/action/resource
   *
   * CLI: --call group/controller/action (--method POST)
   *
   * @param \crcl\base\mvc\request\IConcrete $oRequest
   *
   * @return  string
   */
  public function identifyProperties(\crcl\base\mvc\request\IConcrete $oRequest);
}

abstract class Base implements IConcrete
{
  public function setScheme($aScheme)
  {
    if (\is_array($aScheme))
    {
      //count not possible, because of additional direct overrides [controller:action]
      if (isset($aScheme[1]) && isset($aScheme[2]) && isset($aScheme[3]) && isset($aScheme[4]))
      {
        $this->aScheme = $aScheme;
        return;
      }
    }
    throw new Exception(
      'Invalid scheme.',
      Exception::ERROR_BASE_INVALIDSCHEME, array('scheme' => $aScheme));
  }

  public function getController()
  {
    return $this->sController;
  }

  public function getAction()
  {
    return $this->sAction;
  }

  public function getGroup()
  {
    return $this->sGroup;
  }

  public function getMethod()
  {
    return $this->sMethod;
  }

  public function getResource()
  {
    return $this->sResource;
  }

  public function getView()
  {
    $sView = \crcl\base\String::getInstance($this->sView)
      ->doEndWith('://')
      ->toString();

    return $sView;
  }

  public function hasXhtml()
  {
    return $this->bXhtml;
  }

  public function identifyProperties(\crcl\base\mvc\request\IConcrete $oRequest)
  {
    $this->identifyGroupControllerAction($oRequest);
    $this->identifyMethod($oRequest);
    $this->identifyView($oRequest);
    $this->identifyXhtml($oRequest);
  }

  /** PROTECTED ***********************************************************************************/

  protected $sGroup      = \crcl\base\mvc\Router::DEFAULT_GROUP;
  protected $sController = \crcl\base\mvc\Router::DEFAULT_CONTROLLER;
  protected $sAction     = \crcl\base\mvc\Router::DEFAULT_ACTION;
  protected $sMethod     = \crcl\base\mvc\Router::DEFAULT_METHOD;
  protected $sResource   = \crcl\base\mvc\Router::DEFAULT_RESOURCE;
  protected $sView       = \crcl\base\mvc\Router::DEFAULT_VIEW;
  protected $bXhtml      = \crcl\base\mvc\Router::DEFAULT_XHTML;

  //overriden in Rest router
  protected function identifyView(\crcl\base\mvc\request\IConcrete $oRequest)
  {
    $sView = $oRequest->getView();
    if (!empty($sView))
    {
      $this->sView = $sView;
    }
  }

  /** PRIVATE *************************************************************************************/

  private $aScheme = array(
    1 => 'controller',
    2 => 'controller:action',
    3 => 'group:controller:action',
    4 => 'group:controller:action:resource');

  private function identifyGroupControllerAction(\crcl\base\mvc\request\IConcrete $oRequest)
  {
    $this->reset();

    $sCall = $oRequest->getCall();

    $sCall = \crcl\base\String::getInstance($sCall)
      ->doNotStartWith('/')
      ->doNotEndWith('/');
    $aCall = \explode('/', $sCall);

    $iCount = \count($aCall);
    if (!isset($this->aScheme[$iCount]))
    {
      //get highest array key
      $iCount = \max(\array_keys($this->aScheme));
    }
    $sScheme = $this->aScheme[$iCount];

    $aScheme = \explode(':', $sScheme);

    $aCall = \array_reverse($aCall);
    $aScheme = \array_reverse($aScheme);

    foreach ($aCall as $key => $sPart)
    {
      if (isset($aScheme[$key]))
      {
        if ($sPart !== '')
        {
          $sRoute = 's' . \ucfirst(\strtolower($aScheme[$key]));
          $this->$sRoute = $sPart;
        }
      }
      else
      {
        //highest array key, concat
        $sRoute = 's' .\ucfirst(\strtolower($aScheme[$iCount-1]));
        $this->$sRoute = $sPart . '/' . $this->$sRoute;
      }
    }
  }

  private function identifyMethod(\crcl\base\mvc\request\IConcrete $oRequest)
  {
    $this->sMethod = $oRequest->getMethod();
  }

  private function identifyXhtml(\crcl\base\mvc\request\IConcrete $oRequest)
  {
    $this->bXhtml = $oRequest->hasXhtml();
  }

  private function reset()
  {
    $this->sGroup      = \crcl\base\mvc\Router::DEFAULT_GROUP;
    $this->sController = \crcl\base\mvc\Router::DEFAULT_CONTROLLER;
    $this->sAction     = \crcl\base\mvc\Router::DEFAULT_ACTION;
    $this->sMethod     = \crcl\base\mvc\Router::DEFAULT_METHOD;
    $this->sResource   = \crcl\base\mvc\Router::DEFAULT_RESOURCE;
    $this->sView       = \crcl\base\mvc\Router::DEFAULT_VIEW;
    $this->bXhtml      = \crcl\base\mvc\Router::DEFAULT_XHTML;
  }
}